package servlet;

import config.Action;
import config.ActionMapping;
import config.Forward;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.xmlrules.DigesterLoader;
import parse.DigesterParse;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 中央控制器
 * 所有请求全部交给中央控制器处理
 * 此处解析actions.xml文件,解析操作只做一次
 * 解析完成之后,根据用户访问的请求去解析结果中找到对应配置
 * 根据配置找到该请求所对应的类中的方法
 * 根据方法返回值选择转发或者重定向到指定页面
 */
@WebServlet("*.do")
public class ActionServlet extends HttpServlet {

    private ActionMapping actionMapping;
    private Map<String, Object> actions;

    @Override
    public void init() throws ServletException {
        actions = new HashMap<>();
        Digester digester = DigesterLoader.createDigester(DigesterLoader.class.getClassLoader().getResource("config/rule.xml"));
        actionMapping = new ActionMapping();
        digester.push(actionMapping);
        try {
            digester.parse(DigesterParse.class.getClassLoader().getResourceAsStream("actions.xml"));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取用户在浏览器地址栏输入的请求地址
        String path = request.getServletPath();
        // 由于在actions.xml中配置的请求没有后缀
        // 因此,需要将获取到的请求的后缀去除
        path = path.substring(0, path.lastIndexOf("."));
        // 根据path的值在ActionMapping对象中获取对应的Action
        Action action = actionMapping.findAction(path);
        if (action == null) {
            System.out.println(path + ".do在配置文件中没有找到对应的配置");
            return;
        }
        try {
            // 如果找到对应配置,则根据配置找到执行请求的对象
            // 由于在配置文件中存在多个配置拥有相同的类
            // 因此,为了提供效率,在获取对象的时候可以做成单例的
            // 获取对应类的Class类型
            Class actionClass = Class.forName(action.getType());
            // 通过单例模式获取对应的对象
            // 在Map中存储了对象的信息,其key是type属性值,value是对应的对象
            // 在Map集合中根据type属性值获取对应对象
            Object obj = actions.get(action.getType());
            if (obj == null) {
                // 如果没有获取到数据,则说明尚未创建过该type所对应的对象
                // 根据type属性值创建对象
                obj = actionClass.newInstance();
                // 将创建好的对象存储到Map集合中
                actions.put(action.getType(), obj);
            }
            System.out.println("obj:" + obj);

            // 对象创建成功之后根据配置文件中的配置找到执行该请求的方法
            // 通过反射获取到Method对象
            Method method = actionClass.getMethod(action.getParameter(), HttpServletRequest.class, HttpServletResponse.class);
            // 执行目标方法,获取到方法的返回值
            String result = (String) method.invoke(obj, request, response);
            // 根据方法返回值进行页面的响应的
            // 如果没有返回值,一般是交给Ajax处理,此处暂不关心
            if (result == null) {
                return;
            }
            // 根据方法返回值找到对应的Forward对象
            Forward forward = action.findForward(result);
            // 如果没有找到forward,说明没有对应的配置
            if (forward == null) {
                System.out.println("根据方法返回值" + result + "在配置文件中没有找到对应配置");
                return;
            }
            // 如果找到了配置,则根据redirect属性的值选择重定向或者转发
            if (forward.isRedirect()) {
                // 如果redirect的值为true,则使用重定向进行页面跳转
                // 页面跳转到path属性所对应的页面
                response.sendRedirect(request.getContextPath() + forward.getPath());
            } else {
                // 否则使用转发跳转到path
                request.getRequestDispatcher(forward.getPath()).forward(request, response);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
